/*
 ------------------------------------------------------------------------------
	willy-player - a simple music player based on SDL.

	Copyright (C) 2011 Pierre Veber
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
------------------------------------------------------------------------------
*/

#include "../platform.h"
#include <stdio.h>
#include <shlwapi.h>
#include <direct.h>
#include <shlobj.h>
#include <objidl.h>

#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "../depends/mad/libmad.lib")
#pragma comment(lib, "../depends/mad/libid3tag.lib")
#pragma comment(lib, "../depends/sdl/SDL.lib")
#pragma comment(lib, "../depends/sdl/SDL_Image.lib")
#pragma comment(lib, "../depends/sdl/SDL_ttf.lib")
#pragma comment(lib, "../depends/zlib/zlib.lib")
#endif

/*
 ------------------------------------------------------------------------------
	Trace utils

 ------------------------------------------------------------------------------
*/

void _wp_output_debug(char* lpszString)
{
	OutputDebugStringA(lpszString);
}

/*
 ------------------------------------------------------------------------------
	Path utils

 ------------------------------------------------------------------------------
*/

int _path_exists(char* path)
{
    if (GetFileAttributesA(path) == (DWORD)-1)
    {
        return 0;
    }
    return 1;
}

int _path_is_dir(const char* pszPath)
{
	int is_dir = FALSE;
	char* pDir = (char*)_strdup(pszPath);
	if (pDir != NULL)
	{
		PathUnquoteSpacesA(pDir);
		is_dir = PathIsDirectoryA(pDir);
		free(pDir);
	}
	return is_dir;
}

const char* _path_find_extension(const char* pszPath)
{
	return PathFindExtensionA(pszPath);
}

const char* _path_find_filename(const char* pszPath)
{
	return PathFindFileNameA(pszPath);
}

char* _path_find_dir(const char* pszPath)
{

	char* pDir = (char*)_strdup(pszPath);
	if (pDir != NULL)
	{
		PathUnquoteSpacesA(pDir);
		if (!PathIsDirectoryA(pDir))
		{
			PathRemoveFileSpecA(pDir);
		}
	}
	return pDir;
}

char* _path_append(char* pszPath, char* pszMore)
{
	char* pszRet = (char*)malloc(strlen(pszPath) + strlen(pszMore) + 2);
	if (pszRet != NULL)
	{
		strcpy(pszRet, pszPath);
		PathAppendA(pszRet, pszMore);
	}
	return pszRet;
}

char* _path_extract_subfolder(const char* pszRoot, const char* pszPath)
{
	const char* p = strstr(pszPath, pszRoot);
	const char* rel;
	char* pszSubFolder = NULL;
	size_t s;

	if (p == NULL)
	{
		rel = pszPath;
	}
	else
	{
		rel = p + strlen(pszRoot);
		if ( (*rel == '/') || (*rel == '\\') )
		{
			rel++;
		}
		p = strrchr(pszPath, '\\');
		if (p == NULL)
		{
			p = strrchr(pszPath, '/');
		}
		if (p != NULL)
		{
			s = p - rel;
		}
		else
		{
			s = strlen(rel);
		}
		pszSubFolder = (char*)malloc(s+1);
		if (pszSubFolder != NULL)
		{
			strncpy(pszSubFolder, rel, s);
			pszSubFolder[s] = '\0';
		}
	}
	return pszSubFolder;
}


/*
 ------------------------------------------------------------------------------
	Walk into a directory recursively

 ------------------------------------------------------------------------------
*/

int _walk(char* pszBase, void* pData, FoundFileProc fpf, MallocProc fpm)
{
	WIN32_FIND_DATAA wfd;
	HANDLE hFind;
	int ret = -1;
	
	char* pszFindPath = (char*)malloc(strlen(pszBase)+3);
	if (pszFindPath == NULL)
	{
		return 0;
	}

	strcpy(pszFindPath, pszBase);
	strcat(pszFindPath, "\\*");

	hFind = FindFirstFileA(pszFindPath, &wfd);
	if (hFind != INVALID_HANDLE_VALUE)
	{
		do
		{
			if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)
			{
				if (wfd.cFileName[0] != '.')
				{
					char* pszNewBase = (char*)fpm(strlen(pszBase)+strlen(wfd.cFileName)+2, pData);
					if (pszNewBase != NULL)
					{
						sprintf(pszNewBase, "%s\\%s", pszBase, wfd.cFileName);
						_walk(pszNewBase, pData, fpf, fpm);
					}
				}
			}
			else
			{
				fpf(pData, pszBase, wfd.cFileName);
			}
		} while (FindNextFileA(hFind, &wfd));
	}

	free(pszFindPath);
	FindClose(hFind);

	return 0;
}

/*
 ------------------------------------------------------------------------------
	Configuration file write/read

 ------------------------------------------------------------------------------
*/
#define READ_BUF	512
char* _wp_conf_read(char* pszConfFile, char* pszSection, char* pszKey)
{
	int n = 0;
	DWORD dwChars;
	char* pszValue = NULL;

	do
	{
		n++;
		pszValue = (char*)realloc(pszValue, n * READ_BUF);
		if (!pszValue)
		{
			break;
		}
		dwChars = GetPrivateProfileStringA(pszSection, pszKey, "$", pszValue, n * READ_BUF, pszConfFile);
	}
	while (dwChars >= (DWORD)(n * READ_BUF));

	if ( (pszValue != NULL) && (pszValue[0] == '$') )
	{
		return NULL;
	}

	return pszValue;
}

int _wp_conf_write(char* pszConfFile, char* pszSection, char* pszKey, char* pszValue)
{
	return NULL;
}

/*
 ------------------------------------------------------------------------------
	Unique instance check

 ------------------------------------------------------------------------------
*/

#define MAX_COMDATA_PARAM_LEN	4096

struct ComData_t
{
    char	szParameter[MAX_COMDATA_PARAM_LEN];
	int		termination;
};

ComData_t*	_g_pComData		= NULL;
HANDLE		_g_hMapObject	= NULL;
HANDLE		_g_hNotifyEvent	= NULL;
HANDLE		_g_hUnlockEvent	= NULL;

int _wp_init_instance(void)
{
	int fUnique = -1;

	/* check there is no other willy-player program instance running */
    _g_hMapObject = CreateFileMapping( 
                        INVALID_HANDLE_VALUE,
                        NULL,
                        PAGE_READWRITE,
                        0,
                        sizeof(ComData_t),
                        L"willy-player-shared-memory"
                        );

    if (_g_hMapObject != NULL)
    {
		_g_pComData = (ComData_t*)MapViewOfFile(
						_g_hMapObject,
						FILE_MAP_WRITE,
						0,0,0
						);
		
		if (_g_pComData != NULL)
		{
			fUnique = (GetLastError() != ERROR_ALREADY_EXISTS);
			/* events must be created */
			_g_hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, L"willy-player-com-event");
			if (_g_hNotifyEvent != NULL)
			{
				_g_hUnlockEvent = CreateEvent(NULL, TRUE, TRUE, L"willy-player-unlock-event");
				if ( (_g_hUnlockEvent != NULL) && (_wp_lock_instance_data() != NULL) )
				{
					_g_pComData->termination = 0;
					_g_pComData->szParameter[0] = '\0';
					FlushViewOfFile(_g_pComData, sizeof(ComData_t));
					_wp_release_instance_data();
					return fUnique;
				}
			}
			return -1;
		}
	}

	return fUnique;
}

int _wp_signal_instance(char* cmdline)
{
	if ( (_g_pComData != NULL) && (_g_hNotifyEvent != NULL) )
	{
		/* wait for _g_pComData to be unlocked */
		if ( _wp_lock_instance_data() != NULL )
		{
			strncpy(_g_pComData->szParameter, cmdline, MAX_COMDATA_PARAM_LEN);
			_g_pComData->szParameter[MAX_COMDATA_PARAM_LEN-1] = '\0';
	        
			FlushViewOfFile(_g_pComData, sizeof(ComData_t));
			_wp_release_instance_data();
			SetEvent(_g_hNotifyEvent);
		}
	}
	return 0;
}

int _wp_listen_instance(void)
{
	DWORD dwErr;

    // --- wait for the event to be signalled
	if ( (_g_pComData != NULL) && (_g_hNotifyEvent != NULL) )
	{
        do
        {
            dwErr = WaitForSingleObject(_g_hNotifyEvent, 60000);
            if (dwErr == WAIT_TIMEOUT)
            {
                /* 
					Do something? 
					e.g. check ui thread health?
				*/
            }
            else if (dwErr == WAIT_OBJECT_0)
            {
				return _g_pComData->termination;
            }
        
        } while (1);
    }

	return -1;
}

char* _wp_lock_instance_data(void)
{
	if ( (_g_pComData != NULL) && (_g_hUnlockEvent != NULL) )
	{
		/* wait for _g_pComData to be unlocked */
		if ( WaitForSingleObject(_g_hUnlockEvent, 5000) == WAIT_OBJECT_0 )
		{
			ResetEvent(_g_hUnlockEvent);
			return _g_pComData->szParameter;
		}
	}
	return NULL;
}

int _wp_release_instance_data(void)
{
	if (_g_hUnlockEvent != NULL)
	{
		return (int)SetEvent(_g_hUnlockEvent);
	}
	return 0;
}

int _wp_terminate_instance(void)
{
	if ( (_g_pComData != NULL) && (_g_hNotifyEvent != NULL) )
	{
		/* wait for _g_pComData to be unlocked */
		if ( _wp_lock_instance_data() != NULL )
		{
			_g_pComData->termination = 1;
			_g_pComData->szParameter[0] = '\0';
			FlushViewOfFile(_g_pComData, sizeof(ComData_t));
			_wp_release_instance_data();
			SetEvent(_g_hNotifyEvent);
		}
	}
	return 0;
}

int _wp_free_instance_data(void)
{
	if (_g_pComData != NULL)
	{
		/* free everything */
		UnmapViewOfFile(_g_pComData);
		_g_pComData = NULL;
		CloseHandle(_g_hMapObject);
	}
	if (_g_hNotifyEvent != NULL)
	{
		CloseHandle(_g_hNotifyEvent);
		_g_hNotifyEvent = NULL;
	}
	if (_g_hUnlockEvent != NULL)
	{
		CloseHandle(_g_hUnlockEvent);
		_g_hUnlockEvent = NULL;
	}
	return 0;
}

/*
 ------------------------------------------------------------------------------
	user storage location

 ------------------------------------------------------------------------------
*/

char* _wp_user_store()
{
    LPITEMIDLIST pidl;
	char path[MAX_PATH];
	char* dir = NULL;

    if (SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl) == S_OK)
    {
        if (SHGetPathFromIDListA(pidl, path))
        {
            LPMALLOC pm;

			dir = _strdup(path);
            
			if (SHGetMalloc(&pm) == S_OK)
            {
				pm->Free(pidl);
            }
        }
    }
    return dir;
}

/*
 ------------------------------------------------------------------------------
	Main: windows unicode entry point

 ------------------------------------------------------------------------------
*/

#ifndef _UNICODE
#error "Project must be built in UNICODE."
#endif

int APIENTRY wWinMain(
			HINSTANCE hInstance,
			HINSTANCE hPrevInstance,
			LPWSTR    lpCmdLine,
			int       nCmdShow
			)
{
	/* convert command line to UTF-8 */
	char* pszCmdLine = (char*)malloc(sizeof(char)*(wcslen(lpCmdLine)+1));
	
	WideCharToMultiByte(CP_UTF8, 0, lpCmdLine, -1 ,pszCmdLine, wcslen(lpCmdLine)+1, NULL, NULL);
	PathUnquoteSpacesA(pszCmdLine);

	return _wp_main(pszCmdLine);
}

